<?php
/**
 * Created by PhpStorm.
 * User: 小灰灰
 * Date: 2022-06-16
 * Time: 9:13:30
 * Info:
 */

namespace app\admin\controller\system;

use app\admin\annotation\ControllerAnnotation;
use app\admin\annotation\NodeAnotation;
use app\admin\controller\CommonController;
use app\common\model\Options as OptionsModel;
use app\common\model\AuthMenu as AuthMenuModel;
use Doctrine\Inflector\InflectorFactory;
use Webman\RedisQueue\Redis;
use support\Request;
use think\facade\Db;
use support\Cache;
use support\lib\Backup;
use support\lib\Sql;
use support\lib\Layui;
use support\lib\DebugTrait;
use ZipArchive;
use \Exception;

/**
 * @ControllerAnnotation(title="数据库管理")
 * Class Node
 * @package app\admin\controller\system
 */
class DatabaseController extends CommonController
{

    /**
     * @NodeAnotation(title="数据列表")
     */
    public function index(Request $request)
    {
        if ($request->isAjax()) {
            $list = Db::query("SHOW TABLE STATUS");
            for ($i = 0; $i < count($list); $i++) {
                $list[$i]['Data_length'] = sizeCount($list[$i]['Data_length']);
            }
            $total = count($list);

            return $this->success('ok', $list, $total);
        }

        return view('database/index');
    }

    /**
     * @NodeAnotation(title="数据备份")
     */
    public function backup(Request $request)
    {
        $DataBase  = config('thinkorm');
        $CAdmin    = config('huicmf');
        $backupDir = public_path().DIRECTORY_SEPARATOR.$CAdmin['backupDir'];
        if ($request->isAjax()) {
            try {
                if (empty($DataBase['connections']['mysql'])) {
                    return error_json('获取数据库配置失败');
                }
                $backup = new Backup($DataBase['connections']['mysql']['hostname'],
                    $DataBase['connections']['mysql']['username'], $DataBase['connections']['mysql']['database'],
                    $DataBase['connections']['mysql']['password'], $DataBase['connections']['mysql']['hostport']);
                $backup->setIgnoreTable($CAdmin['backupIgnoreTables'])->backup($backupDir);

                return $this->success('备份成功');
            } catch (\Exception $e) {
                return error_json($e->getMessage());
            }
        }

    }

    /**
     * @NodeAnotation(title="还原数据")
     */
    public function restore(Request $request)
    {
        $BackUp    = config('huicmf');
        $backupDir = public_path().DIRECTORY_SEPARATOR.$BackUp['backupDir'];
        $file      = $request->input('file');
        if ($file) {
            try {
                $dir = public_path().DIRECTORY_SEPARATOR.$BackUp['backupDir'];
                if ( ! is_dir($dir)) {
                    mkdir($dir, 0755);
                }
                $file = $backupDir.$file;
                if (class_exists('ZipArchive')) {
                    $zip = new ZipArchive;
                    if ($zip->open($file) !== true) {
                        throw new Exception('无法打开备份文件');
                    }
                    if ( ! $zip->extractTo($dir)) {
                        $zip->close();
                        throw new Exception('无法解压备份文件');
                    }

                    $filename = basename($file);
                    $sqlFile  = $dir.str_replace('.zip', '.sql', $filename);
                    if ( ! is_file($sqlFile)) {
                        throw new Exception('未找到SQL文件');
                    }
                    $filesize = filesize($sqlFile);
                    $list     = Db::query('SELECT @@global.max_allowed_packet');

                    if (isset($list[0]['@@global.max_allowed_packet']) && $filesize >= $list[0]['@@global.max_allowed_packet']) {
                        Db::execute('SET @@global.max_allowed_packet = '.($filesize + 1024));
                    }
                    $sql = file_get_contents($sqlFile);

                    if (preg_match('/.*;$/', trim($sql))) {
                        try {
                            $sqlArr = array_filter(explode(";\n\n", trim($sql)));
                            foreach ($sqlArr as $k => $v) {
                                $res = Db::execute($v);
                            }
                            unlink($sqlFile);

                            return $this->success('还原成功！！！');
                        } catch (Exception $e) {
                            return error_json($e->getMessage());
                        }
                    } else {
                        return 0;
                    }
                }

                return error_json('非zip包');
            } catch (Exception $e) {
                return error_json($e->getMessage());
            }
        }

        return error_json('错错错，是我的错');
    }

    /**
     * @NodeAnotation(title="备份列表")
     */
    public function databack_list(Request $request)
    {
        $BackUp     = config('huicmf');
        $backupDir  = public_path().DIRECTORY_SEPARATOR.$BackUp['backupDir'];
        $backupList = [];
        foreach (glob($backupDir."*.zip") as $filename) {
            $time              = filemtime($filename);
            $backupList[$time] = [
                'file' => str_replace($backupDir, '', $filename),
                'date' => date("Y-m-d H:i:s", $time),
                'size' => sizeCount(filesize($filename))
            ];

        }
        krsort($backupList);

        return view('database/databack_list', ['data' => $backupList]);
    }

    /**
     * @NodeAnotation(title="删除备份")
     */
    public function delete(Request $request)
    {
        $BackUp    = config('huicmf');
        $backupDir = public_path().DIRECTORY_SEPARATOR.$BackUp['backupDir'];
        $file      = $backupDir.$request->get('file');
        if (file_exists($file)) {
            unlink($file);

            return $this->success('删除成功');
        } else {
            return error_json('删除失败');
        }
    }

    /**
     * @NodeAnotation(title="下载备份")
     */
    public function download(Request $request)
    {
        $BackUp    = config('huicmf');
        $backupDir = public_path().DIRECTORY_SEPARATOR.$BackUp['backupDir'];
        $file      = $request->input('file');
        if (file_exists($backupDir.$file)) {
            return response()->download($backupDir.$file, $file);
        }
    }

    /**
     * @NodeAnotation(title="优化表")
     */
    public function optimize(Request $request)
    {
        $name = $request->input('table');
        if (is_array($name)) {
            $queue = 'optimize_tables';
            $data  = ['name' => $name, 'admin_id' => cmf_get_admin_id()];
            Redis::send($queue, $data);

            $return = '优化表会在后台处理，不用管';
        } else {
            if (Db::execute("OPTIMIZE TABLE `{$name}`")) {
                $return = '优化表 ['.$name.'] 成功';
            } else {
                $return = '优化表 ['.$name.'] 失败';
            }
        }

        return $this->success($return);
    }

    /**
     * @NodeAnotation(title="修复表")
     */
    public function repair(Request $request)
    {
        $name = $request->input('table');
        if (is_array($name)) {
            foreach ($name as $key => $row) {
                Db::execute("REPAIR TABLE `{$row}`");
            }
            $return = '全部修复成功';
        } else {
            if (Db::execute("REPAIR TABLE `{$name}`")) {
                $return = '修复表 ['.$name.'] 成功';
            } else {
                $return = '修复表 ['.$name.'] 成功';
            }
        }

        return $this->success($return);
    }

    public function table_create(Request $request)
    {
        if ($request->isPost()) {
            $data          = $request->post();
            $table_name    = Sql::filterAlphaNum($data['table']);
            $table_comment = $data['table_comment'];
            $columns       = $data['columns'];
            $forms         = $data['forms'];
            $keys          = $data['keys'];

            $primary_key_count = 0;
            foreach ($columns as $index => $item) {
                if ( ! $item['field']) {
                    unset($columns[$index]);
                    continue;
                }
                $columns[$index]['primary_key'] = ! empty($item['primary_key']);
                if ($columns[$index]['primary_key']) {
                    $primary_key_count++;
                }
                $columns[$index]['auto_increment'] = ! empty($item['auto_increment']);
                $columns[$index]['nullable']       = ! empty($item['nullable']);
                if ($item['default'] === '') {
                    $columns[$index]['default'] = null;
                } else {
                    if ($item['default'] === "''") {
                        $columns[$index]['default'] = '';
                    }
                }
            }
            if ($primary_key_count > 1) {
                return $this->error('不支持复合主键');
            }

            foreach ($forms as $index => $item) {
                if ( ! $item['field']) {
                    unset($forms[$index]);
                    continue;
                }
                $forms[$index]['form_show']   = ! empty($item['form_show']);
                $forms[$index]['list_show']   = ! empty($item['list_show']);
                $forms[$index]['enable_sort'] = ! empty($item['enable_sort']);
                $forms[$index]['searchable']  = ! empty($item['searchable']);
            }
            foreach ($keys as $index => $item) {
                if ( ! $item['name'] || ! $item['columns']) {
                    unset($keys[$index]);
                }
            }

            //字段
            $type_method_map = Sql::methodControlMap();
            foreach ($columns as $column) {
                if ( ! isset($column['type'])) {
                    return $this->error("请为{$column['field']}选择类型");
                }
                if ( ! isset($type_method_map[$column['type']])) {
                    return $this->error("不支持的类型{$column['type']}");
                }
            }

            try {
                Sql::createTable($table_name, $columns, $table_comment);
            } catch (Exception $e) {
                return $this->error($e->getMessage());
            }

            // 重建索引
            foreach ($keys as $key) {
                $name       = $key['name'];
                $columns    = is_array($key['columns']) ? $key['columns'] : explode(',', $key['columns']);
                $type       = $key['type'];
                $columnsNew = [];
                foreach ($columns as $val) {
                    $columnsNew[] = "`{$val}`";
                }
                $columnsNewStr = implode(',', $columnsNew);
                if ($type == 'unique') {
                    Db::execute("ALTER TABLE `{$table_name}` ADD UNIQUE ({$columnsNewStr})");
                    continue;
                }
                Db::execute("ALTER TABLE {$table_name} ADD INDEX {$name}({$columnsNewStr})");
            }

            $form_schema_map = [];
            foreach ($forms as $item) {
                $form_schema_map[$item['field']] = $item;
            }
            $form_schema_map = json_encode($form_schema_map, JSON_UNESCAPED_UNICODE);
            $this->updateSchemaOption($table_name, $form_schema_map);

            return $this->success('操作成功');

            /*
             Db::query("ALTER TABLE `{$table_name}` COMMENT '{$table_comment}'");

            // 索引
            foreach ($keys as $key) {
                $name       = $key['name'];
                $columns    = is_array($key['columns']) ? $key['columns'] : explode(',', $key['columns']);
                $type       = $key['type'];
                $columnsNew = [];
                foreach ($columns as $val) {
                    $columnsNew[] = "`{$val}`";
                }
                $columnsNewStr = implode(',', $columnsNew);
                if ($type == 'unique') {
                    Db::query("ALTER TABLE `{$table_name}` ADD UNIQUE ({$columnsNewStr})");
                    continue;
                }
                Db::query("ALTER TABLE {$table_name} ADD INDEX {$name}({$columnsNewStr})");
            }
            $form_schema_map = [];
            foreach ($forms as $item) {
                $form_schema_map[$item['field']] = $item;
            }
            $form_schema_map = json_encode($form_schema_map, JSON_UNESCAPED_UNICODE);
            $option_name     = $this->updateSchemaOption($table_name, $form_schema_map);

            return $this->success('操作成功');
            */
        }

        return view('database/table_create');
    }

    /**
     * @NodeAnotation(title="编辑表")
     */
    public function table_edit(Request $request)
    {
        $table = $request->get('table');
        if ($request->isAjax()) {
            $table         = $request->get('table');
            $return        = Sql::getSchema($table);
            $return['sql'] = Sql::getTableSql($table);

            return $this->success("ok", $return);
        }

        return view('database/table_edit', ['table' => $table]);
    }

    /**
     * @NodeAnotation(title="修改表操作")
     */
    public function modify(Request $request)
    {
        if ($request->isPost()) {
            $data                  = $request->post();
            $old_table_name        = Sql::filterAlphaNum($data['old_table']);
            $table_name            = Sql::filterAlphaNum($data['table']);
            $table_comment         = $data['table_comment'];
            $columns               = $data['columns'];
            $forms                 = $data['forms'];
            $keys                  = ! empty($data['keys']) ? $data['keys'] : [];
            $primary_key           = null;
            $auto_increment_column = null;
            $schema                = Sql::getSchema($old_table_name, null, 0);
            $old_columns           = $schema['columns'];
            $old_primary_key       = $schema['table']['primary_key'][0] ?? null;

            $primary_key_count = $auto_increment_count = 0;
            foreach ($columns as $index => $item) {
                if ( ! $item['field']) {
                    unset($columns[$index]);
                    continue;
                }
                $field                             = $item['field'];
                $columns[$index]['auto_increment'] = ! empty($item['auto_increment']);
                $columns[$index]['nullable']       = ! empty($item['nullable']);
                $columns[$index]['primary_key']    = ! empty($item['primary_key']);
                if ($columns[$index]['primary_key']) {
                    $primary_key                 = $item['field'];
                    $columns[$index]['nullable'] = false;
                    $primary_key_count++;
                }
                if ($item['default'] === '') {
                    $columns[$index]['default'] = null;
                } else {
                    if ($item['default'] === "''") {
                        $columns[$index]['default'] = '';
                    }
                }
                if ($columns[$index]['auto_increment']) {
                    $auto_increment_count++;
                    if ( ! isset($old_columns[$field]) || ! $old_columns[$field]['auto_increment']) {
                        $auto_increment_column = $columns[$index];
                        unset($auto_increment_column['old_field']);
                        $columns[$index]['auto_increment'] = false;
                    }
                }
            }
            if ($primary_key_count > 1) {
                $this->error('不支持复合主键');
            }
            if ($auto_increment_count > 1) {
                $this->error('一个表只能有一个自增字段，并且必须为key');
            }
            foreach ($forms as $index => $item) {
                if ( ! $item['field']) {
                    unset($forms[$index]);
                    continue;
                }
                $forms[$index]['form_show']   = ! empty($item['form_show']);
                $forms[$index]['list_show']   = ! empty($item['list_show']);
                $forms[$index]['enable_sort'] = ! empty($item['enable_sort']);
                $forms[$index]['searchable']  = ! empty($item['searchable']);
            }
            foreach ($keys as $index => $item) {
                if ( ! $item['name'] || ! $item['columns']) {
                    unset($keys[$index]);
                }
            }

            // 改表名
            if ($table_name != $old_table_name) {
                Sql::rename($old_table_name, $table_name);
            }
            $type_method_map = Sql::methodControlMap();
            foreach ($columns as $column) {
                if ( ! isset($type_method_map[$column['type']])) {
                    $this->error("不支持的类型{$column['type']}");
                }
                $field      = $column['old_field'] ?? $column['field'];
                $old_column = $old_columns[$field] ?? [];
                // 类型更改
                foreach ($old_column as $key => $value) {
                    if (key_exists($key,
                            $column) && ($column[$key] != $value || ($key === 'default' && $column[$key] !== $value))) {
                        $this->modifyColumn($column, $table_name);
                        break;
                    }
                }
            }

            //表注释更改
            $table = Sql::getSchema($table_name, 'table', 0);
            if ($table_comment !== $table['comment']) {
                Sql::updateComments($table_name, $table_comment);
            }

            $old_columns = Sql::getSchema($table_name, 'columns', 0);
            foreach ($columns as $column) {
                $field = $column['field'];
                // 新字段
                if ( ! isset($old_columns[$field])) {
                    Sql::alterTableField($table_name, $field, $column['type'], $column['length'], $column['default'],
                        $column['primary_key'], $column['auto_increment'], $column['nullable'], $column['comment']);
                }
            }

            // 更新索引名字
            foreach ($keys as $key) {
                if ( ! empty($key['old_name']) && $key['old_name'] !== $key['name']) {
                    Sql::renameIndex($key['old_name'], $key['name']);
                }
            }

            // 找到删除的字段
            $old_columns         = Sql::getSchema($table_name, 'columns', 0);
            $exists_column_names = array_column($columns, 'field', 'field');
            $old_columns_names   = array_column($old_columns, 'field');
            $drop_column_names   = array_diff($old_columns_names, $exists_column_names);
            $drop_column_names   = Sql::filterAlphaNum($drop_column_names);
            foreach ($drop_column_names as $drop_column_name) {
                Db::query("ALTER TABLE {$table_name} DROP COLUMN `{$drop_column_name}`");
            }
            $old_keys = Sql::getSchema($table_name, 'keys', 0);
            foreach ($keys as $key) {
                $key_name = $key['name'];
                $old_key  = $old_keys[$key_name] ?? [];
                // 如果索引有变动，则删除索引，重新建立索引
                if ($old_key && ($key['type'] != $old_key['type'] || $key['columns'] != $old_key['columns'])) {
                    $old_key = [];
                    unset($old_keys[$key_name]);
                    Db::query("drop index {$key_name} on {$table_name}");
                }
                // 重新建立索引
                if ( ! $old_key) {
                    $name       = $key['name'];
                    $columns    = is_array($key['columns']) ? $key['columns'] : explode(',', $key['columns']);
                    $type       = $key['type'];
                    $columnsNew = [];
                    foreach ($columns as $val) {
                        $columnsNew[] = "`{$val}`";
                    }
                    $columnsNewStr = implode(',', $columnsNew);
                    if ($type == 'unique') {
                        Db::query("ALTER TABLE `{$table_name}` ADD UNIQUE ({$columnsNewStr})");
                        continue;
                    }
                    Db::query("ALTER TABLE {$table_name} ADD INDEX {$name}({$columnsNewStr})");
                }
            }
            // 找到删除的索引
            $exists_key_names = array_column($keys, 'name', 'name');
            $old_keys_names   = array_column($old_keys, 'name');
            $drop_keys_names  = array_diff($old_keys_names, $exists_key_names);
            foreach ($drop_keys_names as $name) {
                Db::query("drop index {$name} on {$table_name}");
            }

            // 变更主键
            if ($old_primary_key != $primary_key) {
                if ($old_primary_key) {
                    Db::query("ALTER TABLE `{$table_name}` DROP PRIMARY KEY");
                }
                if ($primary_key) {
                    $primary_key = Sql::filterAlphaNum($primary_key);
                    Db::query("ALTER TABLE `{$table_name}` ADD PRIMARY KEY(`{$primary_key}`)");
                }
            }

            // 一个表只能有一个 auto_increment 字段，并且是key，所以需要在最后设置
            if ($auto_increment_column) {
                $this->modifyColumn($auto_increment_column, $table_name);
            }

            $form_schema_map = [];
            foreach ($forms as $item) {
                $form_schema_map[$item['field']] = $item;
            }
            $form_schema_map = json_encode($form_schema_map, JSON_UNESCAPED_UNICODE);
            $option_name     = $this->updateSchemaOption($table_name, $form_schema_map);

            return $this->success('操作成功', $option_name);

        }
    }

    /**
     * @NodeAnotation(title="一键CURD")
     */
    public function table_crud(Request $request)
    {
        $table_name     = $request->input('table');
        $table_basename = strpos($table_name, 'cmf_') === 0 ? substr($table_name, 3) : $table_name;
        $inflector      = InflectorFactory::create()->build();
        $model_class    = $inflector->classify($inflector->singularize($table_basename));

        if ($request->isPost()) {
            $table_name      = $request->input('table');
            $title           = $request->post('title');
            $pid             = $request->post('pid', 0);
            $icon            = $request->post('icon', 'layui-icon-circle-dot');
            $controller_file = '/'.trim($request->post('controller', ''), '/');
            $model_file      = '/'.trim($request->post('model', ''), '/');
            $overwrite       = $request->post('overwrite');
            if ($controller_file === '/' || $model_file === '/') {
                return $this->error('控制器和model不能为空');
            }

            $controller_info = pathinfo($controller_file);
            $model_info      = pathinfo($model_file);
            $controller_path = Sql::filterPath($controller_info['dirname'] ?? '');
            $model_path      = Sql::filterPath($model_info['dirname'] ?? '');

            $controller_file_name = Sql::filterAlphaNum($controller_info['filename'] ?? '');
            $model_file_name      = Sql::filterAlphaNum($model_info['filename'] ?? '');

            if ($controller_info['extension'] !== 'php' || $model_info['extension'] !== 'php') {
                return $this->error('控制器和model必须以.php为后缀');
            }

            $pid = (int)$pid;
            if ($pid) {
                $parent_menu = AuthMenuModel::find($pid);
                if ( ! $parent_menu) {
                    return $this->error('父菜单不存在');
                }
            }

            if ( ! $overwrite) {
                if (is_file(base_path($controller_file))) {
                    return $this->error("$controller_file 已经存在");
                }
                if (is_file(base_path($model_file))) {
                    return $this->error("$model_file 已经存在");
                }
            }

            $explode = explode('/', trim($controller_path, '/'));

            if (strpos(strtolower($controller_file), '/controller/') === false) {
                return $this->error('控制器必须在controller目录下');
            }
            if (count($explode) < 2) {
                return $this->error('控制器参数非法');
            }
            if (strtolower($explode[0]) !== 'app') {
                return $this->error('控制器必须在app目录');
            }
            $app = strtolower($explode[1]) !== 'controller' ? $explode[1] : '';

            $model_class     = $model_file_name;
            $model_namespace = str_replace('/', '\\', trim($model_path, '/'));

            // 创建model
            $this->createModel($model_class, $model_namespace, base_path($model_file), $table_name);

            $controller_suffix    = config("app.controller_suffix");
            $controller_class     = $controller_file_name;
            $controller_namespace = str_replace('/', '\\', trim($controller_path, '/'));

            // 创建controller
            $controller_url_name = $controller_suffix && substr($controller_class,
                -strlen($controller_suffix)) === $controller_suffix ? substr($controller_class, 0,
                -strlen($controller_suffix)) : $controller_class;
            $controller_url_name = str_replace('_', '-', $inflector->tableize($controller_url_name));

            array_shift($explode);
            if ($app) {
                array_shift($explode);
            }
            foreach ($explode as $index => $item) {
                if (strtolower($item) === 'controller') {
                    unset($explode[$index]);
                }
            }
            $controller_base = implode('/', $explode);
            $template_path   = $controller_base ? "$controller_base/$controller_url_name" : $controller_url_name;
            $this->createController($controller_class, $controller_namespace, base_path($controller_file), $model_class,
                $model_namespace, $title, $template_path);

            // 创建模版
            $template_file_path = '/app/'.($app ? "$app/" : '').'view/'.$template_path;
            $primary_key        = Sql::getKeyName($table_name);
            $url_path_base      = "/".($app ? "$app/" : '').$template_path;

            $this->createTemplate(base_path($template_file_path), $table_name, $url_path_base, $primary_key,
                "$controller_namespace\\$controller_class");

            $nodeHref = $url_path_base."/index";
            $menu     = AuthMenuModel::where(['node' => $nodeHref])->find();
            if ( ! $menu) {
                $menu = new AuthMenuModel;
            }
            $menu->pid   = $pid;
            $menu->type  = 1;
            $menu->title = $title;
            $menu->icon  = $icon ?: 'layui-icon-circle-dot';
            $menu->node  = $nodeHref;
            $menu->save();
            Cache::clear();

            return $this->success('操作成功');

        }

        $controller = "/app/admin/controller/dev/".$model_class."Controller.php";
        $model      = "/app/common/model/".$model_class.".php";
        $pidList    = AuthMenuModel::getPidMenuList();
        unset($pidList[0]);
        $pidList = array_values($pidList);

        return view('database/table_curd', [
            'table'      => $table_name,
            'controller' => $controller,
            'model'      => $model,
            'pid_list'   => $pidList
        ]);
    }

    private function do_query($sql, $tableName)
    {
        $sqlquery = $sql ? $sql : $this->request->post('sqlquery');
        if ($sqlquery == '') {
            exit(__('SQL can not be empty'));
        }
        $sqlquery  = str_replace("\r", "", $sqlquery);
        $sqls      = preg_split("/;[ \t]{0,}\n/i", $sqlquery);
        $maxreturn = 200;
        $r         = '';
        foreach ($sqls as $key => $val) {
            if (trim($val) == '') {
                continue;
            }
            $val = rtrim($val, ';');
            $r   .= "SQL：<span style='color:green;'>{$val}</span> ";
            if (preg_match("/^(select|explain)(.*)/i ", $val)) {
                DebugTrait::remark("begin");
                $limit = stripos(strtolower($val), "limit") !== false ? true : false;
                //$count = Db::query("SELECT COUNT(*) as hui_count from {$tableName} LIMIT 1");
                $count = Db::table($tableName)->count();
                if ($count > 0) {
                    $resultlist = Db::query($val.(! $limit && $count > $maxreturn ? ' LIMIT '.$maxreturn : ''));
                } else {
                    $resultlist = [];
                }
                DebugTrait::remark("end");
                $time     = DebugTrait::getRangeTime('begin', 'end', 4);
                $rang_mem = DebugTrait::getRangeMem('begin', 'end', 4);

                $usedseconds = "用时 ".$time." 秒；";
                $members     = "消耗内存 ".$rang_mem." <br />";
                if ($count <= 0) {
                    $r .= '返回结果为空';
                } else {
                    $r .= ('共有'.$count.'条记录!').(! $limit && $count > $maxreturn ? ',最大返回'.$maxreturn.'条' : '');
                }
                $r = $r.','.$usedseconds.$members;
                $j = 0;
                foreach ($resultlist as $m => $n) {
                    $j++;
                    if ( ! $limit && $j > $maxreturn) {
                        break;
                    }
                    $r .= "<hr/>";
                    $r .= "<b style='color:#f00'> 记录".$j."</b><br />";
                    foreach ($n as $k => $v) {
                        $r .= "<font color='blue'>{$k}：</font>{$v}<br/>\r\n";
                    }
                }
            } else {
                DebugTrait::remark("begin");
                $count = Db::execute($val);
                DebugTrait::remark("end");
                $time     = DebugTrait::getRangeTime('begin', 'end', 4);
                $rang_mem = DebugTrait::getRangeMem('begin', 'end', 4);
                $r        .= __('Query affected %s rows and took %s seconds', $count, $time, $rang_mem)."<br />";
            }
        }

        return $r;

    }

    /**
     * 更改字段
     *
     * @param $column
     * @param $table
     *
     * @return mixed
     * @throws BusinessException
     */
    protected function modifyColumn($column, $table)
    {
        $table          = Sql::filterAlphaNum($table);
        $method         = Sql::filterAlphaNum($column['type']);
        $field          = Sql::filterAlphaNum($column['field']);
        $old_field      = Sql::filterAlphaNum($column['old_field'] ?? null);
        $nullable       = $column['nullable'];
        $default        = isset($column['default']) ? Sql::filterAlphaNum($column['default']) : '';
        $comment        = $column['comment'];
        $auto_increment = $column['auto_increment'];
        $length         = (int)$column['length'];

        if ($column['primary_key']) {
            $default = null;
        }

        if ($old_field && $old_field !== $field) {
            $sql = "ALTER TABLE $table CHANGE COLUMN `$old_field` `$field` ";
        } else {
            $sql = "ALTER TABLE $table MODIFY `$field` ";
        }

        if (stripos($method, 'integer') !== false) {
            $type = str_ireplace('integer', 'int', $method);
            if (stripos($method, 'unsigned') !== false) {
                $type = str_ireplace('unsigned', '', $type);
                $sql  .= "$type ";
                $sql  .= 'unsigned ';
            } else {
                $sql .= "$type($length) ";
            }
            if ($auto_increment) {
                $column['nullable'] = false;
                $column['default']  = null;
                $sql                .= 'AUTO_INCREMENT ';
            }
        } else {
            switch ($method) {
                case 'string':
                    $length = $length ?: 255;
                    $sql    .= "varchar($length) ";
                    break;
                case 'char':
                case 'time':
                    $sql .= $length ? "$method($length) " : "$method ";
                    break;
                case 'enum':
                    $args = array_map('trim', explode(',', (string)$column['length']));
                    foreach ($args as $key => $value) {
                        $args[$key] = $value;
                    }
                    $sql .= 'enum('.implode(',', $args).') ';
                    break;
                case 'double':
                case 'float':
                case 'decimal':
                    if (trim($column['length'])) {
                        $args    = array_map('intval', explode(',', $column['length']));
                        $args[1] = $args[1] ?? $args[0];
                        $sql     .= "$method($args[0], $args[1]) ";
                        break;
                    }
                    $sql .= "$method ";
                    break;
                default :
                    $sql .= "$method ";
            }
        }
        if ( ! $nullable) {
            $sql .= "NOT NULL ";
        }
        if ($method != 'text' && $default !== null) {
            $sql .= "DEFAULT '$default' ";
        }

        if ($comment !== null) {
            $sql .= "COMMENT '{$comment}' ";
        }
        dump($sql);
        Db::query($sql);

    }

    /**
     * 更新表的form schema信息
     *
     * @param $table_name
     * @param $data
     *
     * @return string
     */
    protected function updateSchemaOption($table_name, $data)
    {
        $option_name = "table_form_schema_$table_name";
        $option      = OptionsModel::where('name', $option_name)->find();
        if ($option) {
            OptionsModel::where(['name' => $option_name])->data(['value' => $data, 'update_time' => time()])->update();
        } else {
            OptionsModel::create([
                'name'        => $option_name,
                'value'       => $data,
                'create_time' => time(),
                'update_time' => time()
            ]);;
        }

        return $option_name;
    }

    /**
     * 创建model
     *
     * @param $class
     * @param $namespace
     * @param $file
     * @param $table
     *
     * @return void
     */
    protected function createModel($class, $namespace, $file, $table)
    {
        mkDirFile($file);
        $table_val  = "'$table'";
        $pk         = 'id';
        $properties = '';
        $timestamps = '';
        $columns    = [];
        try {
            $database = Sql::table_schema();
            $sql      = "select COLUMN_NAME,DATA_TYPE,COLUMN_KEY,COLUMN_COMMENT from INFORMATION_SCHEMA.COLUMNS where table_name = '{$table}' and table_schema = '{$database}'";
            $sqlList  = Db::query($sql);
            foreach ($sqlList as $item) {
                if ($item['COLUMN_KEY'] === 'PRI') {
                    $pk                     = $item['COLUMN_NAME'];
                    $item['COLUMN_COMMENT'] .= "(主键)";
                }
                $type                          = Sql::getType($item['DATA_TYPE']);
                $properties                    .= " * @property $type \${$item['COLUMN_NAME']} {$item['COLUMN_COMMENT']}\n";
                $columns[$item['COLUMN_NAME']] = $item['COLUMN_NAME'];
            }

        } catch (Exception $e) {
            echo $e;
        }

        if ( ! isset($columns['create_time']) || ! isset($columns['update_time'])) {
            $timestamps = <<<EOF
/**
     * Indicates if the model should be timestamped.
     *
     * @var bool
     */
    public \$timestamps = false;
EOF;
        }
        $properties    = rtrim($properties) ?: ' *';
        $model_content = <<<EOF
<?php

namespace $namespace;

/**
$properties
 */
class $class extends Common
{

    /**
     * The primary key associated with the table.
     *
     * @var string
     */
    protected \$pk = '$pk';
    
    $timestamps
    
}
EOF;
        file_put_contents($file, $model_content);
    }

    /**
     * 创建控制器
     *
     * @param $controller_class
     * @param $namespace
     * @param $file
     * @param $model_class
     * @param $model_namespace
     * @param $name
     * @param $template_path
     *
     * @return void
     */
    protected function createController(
        $controller_class,
        $namespace,
        $file,
        $model_class,
        $model_namespace,
        $name,
        $template_path
    ) {
        $model_class_alias = $model_class;
        if (strtolower($model_class) === strtolower($controller_class)) {
            $model_class_alias = "$model_class as {$model_class}Model";
            $model_class       = "{$model_class}Model";
        }
        mkDirFile($file);
        $controller_content = <<<EOF
<?php

namespace $namespace;

use app\admin\annotation\ControllerAnnotation;
use app\admin\annotation\NodeAnotation;
use app\admin\controller\CurdController;
use support\Request;
use support\Response;
use $model_namespace\\$model_class_alias;

/**
 * @ControllerAnnotation(title="$name")
 * Class Node
 * @package $namespace
 */
 class $controller_class extends CurdController{
    /**
     * @var $model_class
     */
    public \$model = null;

    /**
     * 构造函数
     * @return void
     */
    public function __construct()
    {
        \$this->model = new $model_class;
    }
    
    /**
     * @NodeAnotation(title="{$name}列表")
     */
    public function index(Request \$request){
        if (\$request->isAjax()) {
            \$limit = (int)\$request->input('limit', 10);
            \$page  = (int)\$request->input('page', 1);
            \$key   = \$request->input('key');
            \$first = (int)(\$page - 1) * \$limit;
            \$where = function (\$query) use (\$key) {

            };
            \$list  = \$this->model->where(\$where)->limit(\$first, \$limit)->select();
            \$total = \$this->model->where(\$where)->count();

            return \$this->success("ok", \$list, \$total);
        }
        return view('$template_path/index');
    }
    
    /**
     * @NodeAnotation(title="添加")
     */
    public function add(Request \$request){
        if(\$request->isPost()){
            return parent::insert(\$request);
        }
        return view('$template_path/add');
    }
    
    /**
     * @NodeAnotation(title="编辑")
     */
    public function edit(Request \$request)
    {
        if (\$request->isPost()) {
            return parent::update(\$request);
        }
        \$id = \$request->input('id');
        if (empty(\$id)) {
            return \$this->error('参数错误');
        }
        \$data = \$this->model->find(\$id);
        if (\$data->isEmpty()) {
            return \$this->error('获取数据失败');
        }
        \$data = \$data->toArray();

        return view('dev/test/edit', \$data);
    }
    
 }

EOF;
        file_put_contents($file, $controller_content);
    }

    /**
     * 创建模板
     *
     * @param $template_file_path
     * @param $table
     * @param $template_path
     * @param $url_path_base
     * @param $primary_key
     * @param $controller_class_with_namespace
     *
     * @return void
     */
    protected function createTemplate(
        $template_file_path,
        $table,
        $url_path_base,
        $primary_key,
        $controller_class_with_namespace
    ) {

        mkDirFile($template_file_path.'/index.html');
        $code_base        = Sql::controllerToUrlPath($controller_class_with_namespace);
        $code_base        = trim($code_base, '/');
        $form             = Layui::buildForm($table, 'search');
        $html             = $form->html(3);
        $html             = $html ? <<<EOF
<div class="layui-card">
    <div class="layui-card-body">
        <form class="layui-form top-search-from">
            $html
            <div class="layui-form-item layui-inline">
                <label class="layui-form-label"></label>
                <button class="pear-btn pear-btn-md pear-btn-primary" lay-submit lay-filter="table-query">
                    <i class="layui-icon layui-icon-search"></i>查询
                </button>
                <button type="reset" class="pear-btn pear-btn-md" lay-submit lay-filter="table-reset">
                    <i class="layui-icon layui-icon-refresh"></i>重置
                </button>
            </div>
           
        </form>
    </div>
</div>
EOF: '';
        $html             = str_replace("\n", "\n".str_repeat('    ', 2), $html);
        $js               = $form->js(3);
        $table_js         = Layui::buildTable($table, 4);
        $template_content = <<<EOF
{include file='head'/}
<body class="pear-container">
    <!-- 顶部查询表单 -->
    $html
    
    <!-- 数据表格 -->
    <div class="layui-card">
        <div class="layui-card-body">
            <table id="data-table" lay-filter="data-table"></table>
        </div>
    </div>

    <!-- 表格顶部工具栏 -->
    <script type="text/html" id="table-toolbar">
        <button class="pear-btn pear-btn-primary pear-btn-md" data-open="{:__url('{$code_base}/add')}" data-title="新增" data-reload="2">
            <i class="layui-icon layui-icon-add-1"></i>新增
        </button>
        <button class="pear-btn pear-btn-danger pear-btn-md" lay-event="batchRemove" permission="{$code_base}/delete">
            <i class="layui-icon layui-icon-delete"></i>删除
        </button>
    </script>

    <!-- 表格行工具栏 -->
    <script type="text/html" id="table-bar">
        <button class="pear-btn pear-btn-primary pear-btn-sm" title="编辑"
                data-open="{:__url('{$code_base}/edit')}?id={{d.id}}"
                data-title="编辑" data-reload="2">
            <i class="layui-icon layui-icon-edit"></i>
        </button>
        <button class="pear-btn pear-btn-danger pear-btn-sm" title="删除"
                data-delete="{:__url('{$code_base}/delete')}?id={{d.id}}"
                data-title="您确定要删除吗？" data-reload="2">
            <i class="layui-icon layui-icon-delete"></i>
        </button>
    </script>

    {include file='footer'/}
    
    <script>
        // 相关常量
        const PRIMARY_KEY = "$primary_key";
        const SELECT_API = "$url_path_base/select";
        const INSERT_URL = "$url_path_base/add";
        const UPDATE_API = "$url_path_base/edit";
        const DELETE_API = "$url_path_base/delete";
        $js
        // 表格渲染
        layui.use(["table", "form", "common", "popup", "util"], function() {
            let table = layui.table;
            let form = layui.form;
            let $ = layui.$;
            let common = layui.common;
            let util = layui.util;
            $table_js
            // 编辑或删除行事件
            table.on("tool(data-table)", function(obj) {
                if (obj.event === "remove") {
                    remove(obj);
                } else if (obj.event === "edit") {
                    edit(obj);
                }
            });

            // 表格顶部工具栏事件
            table.on("toolbar(data-table)", function(obj) {
                if (obj.event === "refresh") {
                    refreshTable();
                } else if (obj.event === "batchRemove") {
                    batchRemove(obj);
                }
            });

            // 表格顶部搜索事件
            form.on("submit(table-query)", function(data) {
                table.reload("data-table", {
                    where: data.field
                })
                return false;
            });
            
            // 表格顶部搜索重置事件
            form.on("submit(table-reset)", function(data) {
                table.reload("data-table", {
                    where: []
                })
            });

            // 表格排序事件
            table.on("sort(data-table)", function(obj){
                table.reload("data-table", {
                    initSort: obj,
                    scrollPos: "fixed",
                    where: {
                        field: obj.field,
                        order: obj.type
                    }
                });
            });

            // 删除一行
            let remove = function(obj) {
                return doRemove(obj.data[PRIMARY_KEY]);
            }

            // 删除多行
            let batchRemove = function(obj) {
                let checkIds = common.checkField(obj, PRIMARY_KEY);
                if (checkIds === "") {
                    layui.popup.warning("未选中数据");
                    return false;
                }
                doRemove(checkIds.split(","));
            }

            // 执行删除
            let doRemove = function (ids) {
                let data = {};
                data[PRIMARY_KEY] = ids;
                layer.confirm("确定删除?", {
                    icon: 3,
                    title: "提示"
                }, function(index) {
                    layer.close(index);
                    let loading = layer.load();
                    $.ajax({
                        url: DELETE_API,
                        data: data,
                        dataType: "json",
                        type: "post",
                        success: function(res) {
                            layer.close(loading);
                            if (res.code) {
                              return layui.popup.success("操作成功", refreshTable);
                            }else{
                              return layui.popup.failure(res.msg);
                            }
                        }
                    })
                });
            }

            // 刷新表格数据
            window.refreshTable = function(param) {
                table.reloadData("data-table", {
                    scrollPos: "fixed"
                });
            }
        })

    </script>
</body>
</html>

EOF;
        file_put_contents("$template_file_path/index.html", $template_content);

        /*新增页面*/
        $form             = Layui::buildForm($table);
        $html             = $form->html(5);
        $js               = $form->js(3);
        $template_content = <<<EOF
{include file='head'/}
<body>
    <form class="layui-form" action="">

        <div class="mainBox">
            <div class="main-container">
                $html
            </div>
        </div>

        <div class="bottom">
            <div class="button-container">
                <button type="submit" class="pear-btn pear-btn-primary pear-btn-md" lay-submit=""
                    lay-filter="save">
                    提交
                </button>
                <button type="reset" class="pear-btn pear-btn-md">
                    重置
                </button>
            </div>
        </div>
    </form>
{include file='footer'/}
 
<script>
    // 相关接口
    const INSERT_API = "$url_path_base/add";
    $js
    //提交事件
    layui.use(["form", "popup"], function () {
        layui.form.on("submit(save)", function (data) {
            layui.$.ajax({
                url: INSERT_API,
                type: "POST",
                dateType: "json",
                data: data.field,
                success: function (res) {
                    if (res.code) {
                        return layui.popup.success("操作成功", function () {
                            parent.refreshTable();
                            parent.layer.close(parent.layer.getFrameIndex(window.name));
                        });
                    }else{
                        return layui.popup.failure(res.msg);
                    }
                    
                }
            });
            return false;
        });
    });

</script>
</body>
</html>
EOF;
        file_put_contents("$template_file_path/add.html", $template_content);

        /*更新页面*/
        $form             = Layui::buildForm($table, 'update');
        $html             = $form->html(5);
        $js               = $form->js(6);
        $template_content = <<<EOF
<!DOCTYPE html>
<html lang="en">
    {include file='head'/}
<body>
    <form class="layui-form">
        <div class="mainBox">
            <div class="main-container">
                $html
            </div>
        </div>
        <div class="bottom">
            <div class="button-container">
                <button type="submit" class="pear-btn pear-btn-primary pear-btn-md" lay-submit="" lay-filter="save">
                    提交
                </button>
                <button type="reset" class="pear-btn pear-btn-md">
                    重置
                </button>
            </div>
        </div>
    </form>
{include file='footer'/}
    
<script>
// 相关接口
const PRIMARY_KEY = "$primary_key";
const SELECT_API = "$url_path_base/select" + location.search;
const UPDATE_API = "$url_path_base/edit";
// 获取数据库记录
layui.use(["form", "util"], function () {
    let $ = layui.$;
    $.ajax({
        url: SELECT_API,
        dataType: "json",
        success: function (e) { 
            // 给表单初始化数据
            /*layui.each(e.data[0], function (key, value) {
                let obj = $('*[name="'+key+'"]');
                if (key === "password") {
                    obj.attr("placeholder", "不更新密码请留空");
                    return;
                }
                if (typeof obj[0] === "undefined" || !obj[0].nodeName) return;
                if (obj[0].nodeName.toLowerCase() === "textarea") {
                    obj.val(layui.util.escape(value));
                } else {
                    obj.attr("value", value);
                }
            });*/
        }
    });
    $js
});

//提交事件
layui.use(["form", "popup"], function () {
    layui.form.on("submit(save)", function (data) {
        data.field[PRIMARY_KEY] = layui.url().search[PRIMARY_KEY];
        layui.$.ajax({
            url: UPDATE_API,
            type: "POST",
            dateType: "json",
            data: data.field,
            success: function (res) {
                if (res.code) {
                  return layui.popup.success("操作成功", function () {
                    parent.refreshTable();
                    parent.layer.close(parent.layer.getFrameIndex(window.name));
                  });
                }else{
                  return layui.popup.failure(res.msg);
                }

            }
        });
        return false;
    });
});
</script>

</body>

</html>
EOF;
        file_put_contents("$template_file_path/edit.html", $template_content);
    }

}
